﻿using NetCore.Authorization.Users;
using NetCore.Enums;
using NetCore.Extensions.Mapping;
using NetCore.Services;
using NetCore.Users.Dto;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleAppCore
{
    class Program
    {
        //推荐锁 私有 静态 只读 并且是一个Object对象
        //不能为null string，因为null没有内存空间，报错，string在内存分配上是重用的，任意两个对象同样的内容会指向同一块内存
        //不推荐lock(this) //this 锁的是当前类型实例 如果是类中方法，外部锁定一个实例对象，会导致锁住同一个实例，进行排队
        //lock里面的代码不要太多，这里面是单线程的。
        private static readonly object _lock = new object();
        private int iDo = 0;
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            Console.WriteLine($"{typeof(CustomConfigurationManager).Assembly.GetName().Name}");
            Console.WriteLine($"{typeof(CustomConfigurationManager).Assembly.GetName()}");

            {

            }


            #region 拆解含实体类型字段的表达式目录是结构：Expression<Func<User, bool>> exp = (m) => m.Id.ToString().Equals("5");

            {

                //Expression<Func<User, bool>> exp = (m) => m.Id.ToString().Equals("5");
                //拆解步骤：m m.Id  m.Id.ToString()   m.Id.ToString().Equals("5");
                var m = Expression.Parameter(typeof(User), "m");//m参数为User类型
                var strExp = Expression.Constant("5");//"5" 的表达式
                var field = typeof(User).GetProperty("Id");//typeof(User).GetField("Id");
                var fieldExp = Expression.Property(m, field);//m.Id 的表达式 Expression.Field(m, field)
                var toString = typeof(int).GetMethod("ToString", new Type[] { });//对象的无参数的ToString()方法
                var toStringExp = Expression.Call(fieldExp, toString, new Expression[0]);//m.Id.ToString() 无参数的表达式目录树表示
                var equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });//sting类型的Equals("参数")参数为string类型
                var equalsExp = Expression.Call(toStringExp, equals, new Expression[] { strExp });//m.Id.ToString().Equals("5") 的表达式目录树
                var exp = Expression.Lambda<Func<User, bool>>(equalsExp, new ParameterExpression[] { m });
                var result = exp.Compile().Invoke(new User
                {
                    Id = 5,
                    Name = "测试"
                });
                //exp.Compile()(new User{Id = 5,Name = "测试"});//只能执行表示Lambda表达式的表达式目录树，即LambdaExpression或者Expression<TDelegate>类型。如果表达式目录树不是表示Lambda表达式，需要调用Lambda方法创建一个新的表达式

                Console.WriteLine($"m.Id==5表达式目录树执行返回结果:{result}");


            }
            //{
            //    //拼装表达式目录树，交给下端用
            //    var parameterExp = Expression.Parameter(typeof(User), "m");
            //    var propertyExp = Expression.Property(parameterExp, typeof(User).GetProperty("Level"));
            //    var constantExp = Expression.Constant(18, typeof(int));
            //    var binary = Expression.GreaterThan(propertyExp, constantExp);//Level>5
            //    var lambda = Expression.Lambda<Func<User, bool>>(binary, new ParameterExpression[] { parameterExp });//m.Level>5
            //    var boolResult = lambda.Compile()(new User { Level = TreeNodeLevel.五级 });
            //    Console.WriteLine($"m.Level>5表达式目录树执行返回结果:{boolResult}");
            //}


            #endregion

            #region 利用拆解表达式目录树编写Mapper 性能测试 

            {

                People people = new People()
                {
                    Id = 11,
                    Name = "Eleven",
                    Age = 31
                };
                PeopleCopy peopleCopy = new PeopleCopy()
                {
                    Id = people.Id,
                    Name = people.Name,
                    Age = people.Age
                };

                var pepCopy = ExpMapper<People, PeopleCopy>.Trans(people);

                User user = new User
                {
                    Id = 11,
                    Name = "Chensq",
                    //Account = "Admin",
                    Password = "123456",
                    //Email = "290668617@qq.com",
                    LastLoginTime = DateTime.Now,
                    Level = TreeNodeLevel.九级,
                    UserType = UserType.Financial,
                    Status = UserStatus.Active
                };


                var result = ExpMapper<User, UserDto>.Trans(user);
                Console.WriteLine($"泛型表达式目录树映射返回结果:{result.Name}");
                result = ExpMapper<User, UserDto>.Trans(new User { Id = 2, Name = "Test2" });
                Console.WriteLine($"泛型表达式目录树映射返回结果:{result.Name}");

                //long common = 0;
                //long generic = 0;
                //long cache = 0;
                //long reflection = 0;
                //long serialize = 0;
                //var dtoList = new List<UserDto>();
                //{
                //    Stopwatch watch = new Stopwatch();
                //    watch.Start();
                //    for (int i = 0; i < 100000; i++)
                //    {
                //        var copy = new UserDto()
                //        {
                //            Id = user.Id,
                //            Name = user.Name,
                //            Level = user.Level
                //        };
                //    }
                //    watch.Stop();
                //    common = watch.ElapsedMilliseconds;
                //}
                //{
                //    Stopwatch watch = new Stopwatch();
                //    watch.Start();
                //    for (int i = 0; i < 100000; i++)
                //    {
                //        var copy = ExpMapper<User, UserDto>.Trans(user);
                //    }
                //    watch.Stop();
                //    generic = watch.ElapsedMilliseconds;
                //}
                ////{
                ////    Stopwatch watch = new Stopwatch();
                ////    watch.Start();
                ////    for (int i = 0; i < 100000; i++)
                ////    {
                ////        var copy = ReflectionMapper.Trans<User, UserDto>(user);
                ////    }
                ////    watch.Stop();
                ////    reflection = watch.ElapsedMilliseconds;
                ////}
                //{
                //    Stopwatch watch = new Stopwatch();
                //    watch.Start();
                //    for (int i = 0; i < 100000; i++)
                //    {
                //        dtoList.Add(SerializeMapper.Trans<User, UserDto>(user));
                //    }
                //    watch.Stop();
                //    serialize = watch.ElapsedMilliseconds;
                //}
                //{
                //    Stopwatch watch = new Stopwatch();
                //    watch.Start();
                //    for (int i = 0; i < 100000; i++)
                //    {
                //        var copy = ExpressionMapper.Trans<User, UserDto>(user);
                //    }
                //    watch.Stop();
                //    cache = watch.ElapsedMilliseconds;
                //}

                //Console.WriteLine($"common = { common} ms");
                //Console.WriteLine($"generic = { generic} ms");
                ////Console.WriteLine($"reflection = { reflection} ms");
                //Console.WriteLine($"serialize = { serialize} ms");
                //Console.WriteLine($"cache = { cache} ms");
            }

            #endregion

            #region 拼装表达式目录树 数据结构

            {
                //sql查询的时候，各种查询条件
                //属性可能Id，Name...
                //Expression<Func<User,bool>> lamda = x => x.Level > 5;


            }

            #endregion

            #region 拼装表达式目录树 应用 表达式链接

            {
                //Expression<Func<People, bool>> lambda1 = x => x.Age > 5;
                //Expression<Func<People, bool>> lambda2 = x => x.Id > 5;
                //Expression<Func<People, bool>> lambda3 = lambda1.And(lambda2);
                //Expression<Func<People, bool>> lambda4 = lambda1.Or(lambda2);
                //Expression<Func<People, bool>> lambda5 = lambda1.Not();

                //List<People> peoples = new List<People>()
                //{
                //    new People(){Id=4,Name="123",Age=4},
                //    new People(){Id=5,Name="234",Age=5},
                //    new People(){Id=6,Name="345",Age=6},
                //};
                //var list0 = peoples.Where(lambda3.Compile()).ToList();
                //var list = peoples.WhereBy<People>(lambda3);//扩展与上面等价
                //var query0=peoples.AsQueryable().Where(lambda4);
                //var query = peoples.AsQueryable().WhereBy(lambda5);//扩展与上面等价
            }
            #endregion

            #region MD5 Md5公开的算法，任何语言实现后其实都一样，通用的

            //{
            //    Console.WriteLine(MD5Encrypt.Encrypt("1"));
            //    Console.WriteLine(MD5Encrypt.Encrypt("1"));
            //    Console.WriteLine(MD5Encrypt.Encrypt("123456小李"));
            //    Console.WriteLine(MD5Encrypt.Encrypt("113456小李"));
            //    Console.WriteLine(MD5Encrypt.Encrypt("113456小李113456小李113456小李113456小李113456小李113456小李113456小李"));
            //    string md5Abstract1 = MD5Encrypt.AbstractFile(@"D:\Projects\NetCore\NetCore.sln");
            //    string md5Abstract2 = MD5Encrypt.AbstractFile(@"D:\Projects\NetCore\NetCore.sln");
            //    //1 防止看到明文    
            //    //密码--md5一下--保存密文--登陆的时候--输入的密码也md5一下--比对
            //    //md5不能解密？不能解密  网上的解密都是基于样本比对
            //    //密码复杂点  加盐(密码+ruanmou 再MD5)(双MD5)

            //    //文件摘要，只有相同文件才能相同的md5
            //    //  防篡改：下载VS安装文件(MD5)--官网--软件站下载(MD5) 
            //    //急速秒传：百度云--一瞬间就传完了--第一次上传，传完保存md5--先计算md5--比对--匹配了就不需要上传
            //    // git/svn：源码管理器svn--即使断网了，文件有任何改动都能被发现--本地存了一个文件的MD5--文件有更新，就再对比下MD5

            //    //任何数据MD5后结果都不一样，到目前为止还没碰到  2的128次方
            //    //防抵赖：文章--md5(权威机构)

            //}

            #endregion

            #region Des

            //{
            //    //可逆对称加密 
            //    //数据传输   加密速度快   密钥的安全是问题
            //    string desEn = DesEncrypt.Encrypt("王殃殃");
            //    string desDe = DesEncrypt.Decrypt(desEn);
            //    string desEn1 = DesEncrypt.Encrypt("张三李四");
            //    string desDe1 = DesEncrypt.Decrypt(desEn1);
            //}

            #endregion

            #region Rsa  公开算法 即使拿到密文 你是推算不了密钥 也推算不了原文 加密钥&解密钥是一组的  

            //{
            //    //可逆非对称加密 
            //    //加密解密速度不快  安全性好
            //    KeyValuePair<string, string> encryptDecrypt = RsaEncrypt.GetKeyPair();
            //    string rsaEn1 = RsaEncrypt.Encrypt("net", encryptDecrypt.Key);//key是加密的
            //    string rsaDe1 = RsaEncrypt.Decrypt(rsaEn1, encryptDecrypt.Value);//value 解密的   不能反过来用的
            //    //加密钥  解密钥  钥匙的功能划分

            //    //公钥    私钥      公开程度划分

            //    //根据需求  可以交错的

            //    //网站：1、保密 只有你和你的目标能看懂内容
            //    //      2、完整 不可篡改
            //    //      2、可获得 信息可以拿到
            //    //CA证书：  CA机构作为中间权威机构 非营利性机构 基础信任（浏览器内置证书，全球公认无条件相信）
            //    //1、网站所属公司向CA机构申请证书
            //    //2、CA机构生成公司或网站的基础信息，公钥(解密key)和证书的数字签名
            //    //3、CA把公司的基本信息MD5摘要加密得到一个唯一值，CA自己再非对称可逆加密一下，只有CA才知道加密key,保证信息来源于CA
            //    //4、CA把解密key内置在证书里，公司网站从内置证书里通过解密key解密成功得到的密文确定来自于CA 并可看到CA加密的该公司证书信息
            //    //5、用户使用浏览器客户端(内置根证书)请求（导入了申请的CA证书的）网站， 网站发给用户一个自己的公钥(加密key)
            //    //6、用户拿到加密key加密密文传输给公司网站，公司网站使用自己的私钥(解密key)解密密文，这样只有用户发送给该公司的加密密文才可以被解开

            //}
            #endregion

            #region 多线程

            {
                //进程：计算机概念，程序在服务器运行时占据全部计算资源总合（虚拟的）
                //线程：计算机概念，进程在响应操作时最小单位，也包含cpu 内存 网络 硬盘IO(虚拟概念)
                //一个进程会包含多个线程，线程隶属于进程，进程销毁线程也没了
                //句柄：long数字，是操作系统标识应用程序
                //多线程：计算机概念，一个进程有多个线程同时运行


                //C#里面的多线程：
                //Thread类是C#语言对线程对象的一个封装

                //1、多个cpu的核可以并行工作，多个模拟线程
                //     4核8线程，这里的线程指的是模拟线程
                //2、cpu分片，1s的处理能力分成1000份，系统调度着去响应不同的任务
                //     从宏观角度来说：感觉就是多个任务在并发执行
                //     从微观角度来说，一个物理cpu同一时刻只能为一个任务服务

                //并行与并发：
                //并行：多核之间叫并行
                //并发：cpu分片的并发

                //同步异步：
                //    同步方法：发起调用，完成之后才继续下一行，非常符合逻辑开发
                //              同步方法卡界面，UI线程负责整个程序计算，耗时长且必须全部执行完毕才可以响应别的操作，无暇他顾
                //              有序进行
                //    异步方法：发起调用，不等待完成，直接进入下一行，启动一个新的线程完成方法的计算
                //              异步方法不卡界面，UI线程闲置或不让UI线程执行耗时运算，立即即可完毕，可以立即响应其它操作
                //              如：web应用发个短信通知，异步多线程去发短信 
                //              多线程其实是使用更多资源去换性能
                //              一个订单表统计很耗时间，能不能用多线程优化性能？不能！因为这就是一个操作，没法细分线程并行
                //              需要查询数据库/调用接口/读硬盘文件/做数据计算，能不能多线程优化性能？可以，多个线程任务可以并行
                //              线程不是越多越好，因为资源有限，而且调用有损耗
                //              无序启动线程，无序结束(谁先干完谁先结束)，同一个任务同一个线程，执行时间也不确定，cpu分片
                //              使用多线程请一定小心，很多事儿不是想当然的，尤其是多线程操作间有顺序要求的时候，
                //              通过延迟一点启动来控制顺序？或者预计下结束顺序？这些都不靠谱！

                //需要控制多线程顺序：Task类提供了很多api 注意core里不支持BeginInvoke

                //Action action = () =>
                //{
                //    Console.WriteLine($"委托执行******{Thread.CurrentThread.ManagedThreadId}{DateTime.Now:yyyy/MM/dd HH:mm:ss}************");
                //};
                //AsyncCallback callback = ar =>
                //    {
                //        Console.WriteLine(
                //            $"btnAsyncAdvanced_Click计算完毕，parameter:{ar.AsyncState?.ToString()}***{Thread.CurrentThread.ManagedThreadId}{DateTime.Now:yyyy/MM/dd HH:mm:ss}************");
                //    };
                //var asyncResult = action.BeginInvoke(callback, null);


                ////通过IsCompleted 等待，卡界面--主线程等待，边等待边提示
                //int i = 0;
                //while (!asyncResult.IsCompleted)
                //{
                //    if (i < 9)
                //    {
                //        Console.WriteLine($"等待{++i * 10}%******{Thread.CurrentThread.ManagedThreadId}{DateTime.Now:yyyy/MM/dd HH:mm:ss}************");
                //    }
                //    else
                //    {
                //        Console.WriteLine($"完毕{++i * 10}%******{Thread.CurrentThread.ManagedThreadId}{DateTime.Now:yyyy/MM/dd HH:mm:ss}************");
                //    }
                //    Thread.Sleep(200);
                //}

                //asyncResult.AsyncWaitHandle.WaitOne();//直接等待任务完成
                //asyncResult.AsyncWaitHandle.WaitOne(-1);//无限期等待任务完成
                //asyncResult.AsyncWaitHandle.WaitOne(5000);//最多等待5s

                //action.EndInvoke(asyncResult);//等待某次异步调用操作结束，即时等待,EndInvoke还可以获取委托的返回值

                // Console.WriteLine($"btnAsyncAdvanced_Click开始计算{Thread.CurrentThread.ManagedThreadId}{DateTime.Now:yyyy/MM/dd HH:mm:ss}************");


                // Func<int> fun = () =>
                // {
                //     Console.WriteLine($"执行***{Thread.CurrentThread.ManagedThreadId}{DateTime.Now:yyyy/MM/dd HH:mm:ss}************");
                //    return DateTime.Now.Hour;
                // };
                // int iResult = fun.Invoke();//
                // //IAsyncResult fResult = fun.BeginInvoke(ar => { }, "参数");
                //// var retunValue = fun.EndInvoke(fResult);
                // Console.WriteLine($"执行结果：{iResult}***{Thread.CurrentThread.ManagedThreadId}{DateTime.Now:yyyy/MM/dd HH:mm:ss}************");

                // //一个异步操作只能EndInvoke一次
            }
            {
                //多线程并发任务，某个失败后，希望通知别的线程，都停下来，怎么解决？
                //cts初始化有个IsCancellationRequested属性默认为false
                var cts = new CancellationTokenSource();//cts 线程安全
                TaskFactory taskFactory = new TaskFactory();
                var taskList = new List<Task>();
                for (int i = 0; i < 50; i++)
                {
                    //name 是闭包里面的变量，一次循环只有一个name(新内存空间)，
                    //而i是循环变量，始终只有一个内存区域，显示输出始终等于循环完毕后的值
                    string name = string.Format("btnThreadCore_Click{0}", i);
                    Action<object> act = t =>
                    {
                        try
                        {
                            //if (cts.IsCancellationRequested)
                            //{
                            //    Console.WriteLine("{0} 取消一个任务的执行", t);
                            //}
                            Thread.Sleep(2000);
                            if (t.ToString().Equals("btnThreadCore_Click11"))
                            {
                                throw new Exception(string.Format($"{t} 执行失败"));
                            }
                            if (t.ToString().Equals("btnThreadCore_Click12"))
                            {
                                throw new Exception(string.Format($"{t} 执行失败"));
                            }
                            if (cts.IsCancellationRequested)//检查信号量
                            {
                                Console.WriteLine($"{t} 放弃执行");
                                return;
                            }
                            else
                            {
                                Console.WriteLine($"{t} 执行成功");
                            }
                        }
                        catch (Exception ex)
                        {
                            cts.Cancel();
                            Console.WriteLine(ex.Message);
                        }
                    };
                    taskList.Add(taskFactory.StartNew(act, name, cts.Token));
                }
                Task.WaitAll(taskList.ToArray());
                //1、准备CancellationTokenSource 2、try-catch-cts.cancel 3、action要随时判断IsCancellationRequested
                //2.1、启动线程传递Token 2.2、异常抓取 

            }

            {
                //线程安全问题一般都是有全局变量/共享变量/静态变量/硬盘文件/数据库的值，只要多线程都能访问和修饰
                //最好自己做好数据分拆，避免多线程操作同一数据


                var iNumSync = 0;//线程安全
                var iNumASync = 0;//非线程安全
                var iListAsync = new List<int>();//非线程安全 因为有可能多个线程同时在一块内存空间写操作

                for (int i = 0; i < 10000; i++)
                {
                    iNumSync++;
                }
                for (int i = 0; i < 10000; i++)
                {
                    int k = i;
                    lock (_lock)//lock锁住_lock变量的内存引用空间，确保一次只有一个线程在操作
                    {
                        //Task.Run(()=>iNumASync++);
                        Task.Run(() => iListAsync.Add(k));
                    }
                }
                Thread.Sleep(5 * 1000);

                Console.WriteLine($"***iNumSync={iNumSync},iNumASync={iNumASync};{Thread.CurrentThread.ManagedThreadId}{DateTime.Now:yyyy/MM/dd HH:mm:ss}************");

                //线程安全集合
                //System.Collections.Concurrent.ConcurrentQueue<int>

            }
            {
                //await/async 

            }

            #endregion

            #region CLR内存回收
            {
                //托管资源垃圾回收--CLR提供GC
                //1、什么样的对象需要垃圾回收？托管资源+引用类型

                //托管资源和非托管资源
                //托管资源就是CLR控制的，new的对象，string字符串，变量
                //非托管资源不是CLR控制的，数据库连接，文件流，句柄，打印机连接；
                //Using(SqlConnection)被C#封装了管理哪个非托管的数据库连接资源，
                //只要是手动释放的，都是非托管的
                //2、哪些对象的内存，能被GC回收？对象访问不到了，那就可以被回收了(脱离作用域大括号后访问不到的对象)
                //3、静态变量在内存中唯一并且不会被释放的，它的生命周期是应用程序级，
                //例如web项目，只有网站重启才会被回收 
                //静态变量中的引用类型都是不被回收的，总之离开作用域后不能访问的都被回收。
                //4、对象是如何分配到堆上的？什么时候发生GC？
                //连续分配在堆上，每次new对象分配空间先检查空间够不够，如果不够就会发生GC
                //a)new 对象时--临界点
                //b)GC.Collect() 强制GC 
                //c)程序退出时会GC
                //GC.Collect();//频繁GC是不好的，GC是全局的
                //项目中有6个小时才运行new一次，什么时候GC？不GC，可以手动GC
                //5、GC的过程是怎样的？
                //N个对象--全部对象标记为垃圾--入口开始遍历--访问到的就标记(+1)
                //--遍历完就清理内存--产生不连续内存--压缩--地址移动--修改变量指针地址(这是全局阻塞)--完毕后正常
                //清理内存分2种情况：
                //a)无析构函数，直接清理内存
                //b)把对象转移到一个的单独的队列，会有个析构器线程专门做这个，
                //通常在析构函数内部用来做非托管资源释放，因为CLR肯定调用，所以避免使用者忘记的情况
                //6、垃圾回收策略
                //对象分代：3代
                //0代：对象第一次分配到堆就是0代对象
                //1代：经历了第一次GC之后依然还在内存的对象
                //2代：经历了第二次或以上GC仍驻内存的对象
                //垃圾回收时，优先回收0代对象，提升效率，再依次去回收1，2代对象--再不够就真内存不够了5555
                //大对象堆策略--内存移动大对象；0代空间问题；所以 (80000字节以上都叫大对象，没有分代就直接2代)

                //析构函数&Dispose
                //析构函数：被动清理
                //Dispose：CLR主动清理

            }
            #endregion

            #region 缓存

            //打开一个网页的过程：
            //浏览器--请求--服务器--处理请求发响应--浏览器展示
            //Http协议：数据传输的格式
            //信息是否缓存：一定是服务器控制的
            //ResponseHeader--Cache-Control 来指定下缓存策略
            //1、客户端缓存：缩短网络路劲，加快响应速度；减少请求，降低服务器压力
            //客户端缓存只影响当前用户
            //2、DNS缓存：互联网的第一跳，DNS缓存就是CDN，内容分发网络（CDN加速就是CDN缓存）：cdn缓存影响的是一批用户
            //CDN的要点：将请求路由到就近的CDN节点，以提高响应速度。
            //你的域名dns指向到提供cdn加速的服务商做解析？
            //反向代理：通过服务器访问外网，翻墙， 反过来:同样缩短网络路劲，加快响应速度；减少请求，降低服务器压力：针对全部用户
            //1、隔离网络2、网络加速，反向代理双网卡(电信，联通)3、负载均衡4、缓存(跟CDN，也是识别一下header，压缩到一个物理路径/内存)
            //


            //1、removeAll
            //2、添加缓存时，key带上规则，比如包含_menu_ 清理时就只删除指定keys的缓存数据

            //过期策略：1、永久有效2、过期时间3、滑动过期

            //多线程缓存
            //{
            //    List<Task> taskList=new List<Task>();
            //    for (int i = 0; i < 1000; i++)
            //    {
            //        int k = i;
            //        taskList.Add(Task.Run(()=>CustomCache.Add($"TestKey_{k}",$"TestValue_{k}",10)));
            //        Task.WaitAll(taskList.ToArray());
            //    }
            //}

            //数据集合问题
            //1、数组 内存中连续存储 节约空间 可以索引访问 读取快，增删慢
            //2、链表 内存中非连续摆放，存储数据+地址，只能顺序查找数据，读取慢，增删快
            //3、队列 Queue 先进先出，放任务延迟执行，A不断写入日志任务，B不断取任务去执行

            //IEnumerable 任何数据集合都实现了它，为不同的数据结构提供了统一的访问方式 迭代器模式
            //IQueryable  表达式目录树与IQueryableProvider

            //分布式缓存：
            //MemeryCashed 最早流行
            //Nosql-Redis 主流方案
            //Nosql:非关系型数据库，Not，Only，Sql
            //数据库的关系复杂：好友关系（qq好友100个，映射表，数据冗余，关系型数据库开始累赘）
            //所以就产生了NoSql了
            //特点：基于内存：
            //没有严格的数据格式，不是一行数据的列必须一样
            //丰富的类型，满足web2.0的需求
            //Redis：Remote Dictionary Server 远程字典服务器 基于内存管理(数据存在内存)
            //实现了5种数据结构(分别对应各种需求)，单线程模型的应用程序，对外提供插入-查询-固化-集群功能
            //RDM：可视化操作Redis数据的管理工具(SqlClient)
            //基于内存管理;速度快，Redis还有个固化数据的功能，。VirtualMemory，把一些不经常访问存放在硬盘
            //AOF：数据变化的记录日志，很少用
            //Redis毕竟不是数据库，只能用来提升性能，不能作为数据库
            //单线程模型：nodejs单线程，整个进程只有一个线程，线程就是执行流，性能低？实际上并非如此
            //一次网络请求操作==正则解析请求+计算+数据库操作(发命令--等结果)，单线程都是用事件驱动，发起命令就做下一件事儿，这个线程是完全不做等待的，一直在计算，单线程非常高。
            //单线程多进程的模式来提供集群服务
            //单线程最大的好处就是原子性操作（事务性）
            //ServiceStack(1小时3600次请求--可破解)---意味着net的Ado.Net 其实更像ORM
            //StackExchange(免费) 封装了连接+命令 更像ORM
            //Redis 5大数据结构
            //1、String key-value 缓存 value不超过512MB
            //2、Hash  多个key-string类型的value最小是512byte，即使只保存一个1，也要占用512byte空间
            // 而hash是zipmap存储的，紧密排列
            //3、Set 去重：IP统计去重：添加好友申请，投票限制，点赞，共同好友，推荐好友
            //4、ZSet 去重   而且自带排序   排行榜/统计全局排行榜
            //5、List 

            //ServiceStack://破解,找到3600这行代码，把它干掉
            //SetAll&&AppendToValue&&GetValue&&GetAndSetValue&&IncrementValueBy,这些看上去是组合命令，但实际上是一个具体的命令，原子性命令(事务性)
            //使用Redis能够拦截无效的请求，如果这一层没有，所有的请求压力都到数据库
            //Redis为开发而生，为各种需求提供解决方案。
            //
            #endregion

            #region webApi

            //跨域请求：浏览器请求时，如果A网站(域名+端口号)页面里面，通过XHR请求B域名，这个就是跨域
            //这个请求是可以正常到达B网站服务器后端，正常相应(200)，但是浏览器不允许这样操作，除非
            //有声明(Access-Control-Allow-Origin),
            //浏览器同源策略：处于安全考虑，浏览器限制脚本去发起跨站请求，但是，页面是js/css/图片/iframe 
            //这些是浏览器自己发起的，可以跨域 (可以)
            //JSONP:脚本标签自动请求--请求回来的内容执行个回调方法--解析数据
            //CORS:跨域资源共享，允许服务器在相应头里指定Access-Control-Allow-Origin,浏览器按照相应来操作
            //[EnableCores(origins:"http://localhost:8090/",header:"")]

            #endregion

            #region 数据库
            //数据库：把东西有序放好还能随时找到的工具 它是一个应用程序，数据固化在硬盘上
            //sqlserver自增int，默认聚集索引，一个表聚集索引只有一个(默认主键)，但是可含多个字段
            //非聚集索引：不影响数据的物理排序，但是重复存储一个数据和位置 一张表可以有多个非聚集索引
            //找数据：先找索引--快速定位--拿到数据
            //优化：
            //1、避免对列的计算，任何形式都要避免
            //2、in，or查询，索引会失效，可能是拆分
            //3、in 换 exists
            //4、not in 不要用，不走索引 is null 和 is not null <>都不走索引 可以拆分为< 和>

            #endregion

            #region EF6 

            //LinqToSql包含对象的CRUD操作的API Mapping 并不是ORM就不能写sql
            //dapper  
            //IBatis.Net
            //sugar 
            //sql--反射生成sql--自动执行--反射绑定结果
            //缓存(占内存)
            //开发快捷，降低学习成本
            //sql固定生成，但是僵化，对索引引用不够好，分页算法就不够好，在复杂情况下不好应付
            //ORM工具一般也可以支持写SQL 
            //ORM一般还能适应不同数据库的迁移
            //ORM只是一个工具，去完成它擅长的事儿

            //ORM(对象关系映射):可以理解为一个封装/代理/面向对象的方式来操作数据，会把整个数据库搬到程序里面来
            //封装-->映射  通过这些类就实现对数据的操作    目前EntityFramwork6 支持多种数据，函数，存储过程等
            //跟vs项目完美结合
            //codeFirst:CodeFistFromDB

            try
            {

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                throw new Exception(e.Message);
            }

            #endregion
        }


        /// <summary>
        /// 死锁？
        /// </summary>
        private void DoTest()
        {
            lock (this)//单线程不会死锁,因为this变量区域被单线程这一个线程占据，但会死循环，多线程就死锁
            {
                iDo++;
                if (DateTime.Now.Day < 28 && iDo < 10)
                {
                    Console.WriteLine($"第{iDo++}次调用自己{DateTime.Now.Day}");
                    this.DoTest();
                }
                else
                {
                    Console.WriteLine($"第{iDo++}次调用自己：28号课程结束{DateTime.Now.Day}");
                }
            }
        }
    }
}
